#include <iostream>
#include <vector>
#include <cmath>
#include <complex>


#include "Public/tgaimage.h"
#include "Public/model.h"
#include "Public/geometry.h"


const TGAColor white = TGAColor(255, 255, 255, 255);
const TGAColor red = TGAColor(255, 0, 0, 255);
const TGAColor green = TGAColor(0,   255, 0,   255);
const int width  = 800;
const int height = 800;


Vec3f world2screen(Vec3f v)
{
	return Vec3f(int((v[0] + 1.)*width/2.+.5), int((v[1] + 1.)*height/2.+.5), v[2]);
}

//画线算法
void line(int x0, int y0, int x1, int y1, TGAImage &image, TGAColor color)
{
	bool steep = false;
	if (std::abs(x0-x1)<std::abs(y0-y1)) {
		std::swap(x0, y0);
		std::swap(x1, y1);
		steep = true;
	}
	if (x0>x1) {
		std::swap(x0, x1);
		std::swap(y0, y1);
	}

	for (int x=x0; x<=x1; x++) {
		float t = (x-x0)/(float)(x1-x0);
		int y = y0*(1.-t) + y1*t;
		if (steep) {
			image.set(y, x, color);
		} else {
			image.set(x, y, color);
		}
	}
}

//线扫描算法
void triangle(Vec2i t0, Vec2i t1, Vec2i t2, TGAImage &image, TGAColor color)
{
	if (t0.y==t1.y && t0.y==t2.y) return; // i dont care about degenerate triangles
	if (t0.y>t1.y) std::swap(t0, t1);
	if (t0.y>t2.y) std::swap(t0, t2);
	if (t1.y>t2.y) std::swap(t1, t2);
	int total_height = t2.y-t0.y;
	for (int i=0; i<total_height; i++) {
		bool second_half = i>t1.y-t0.y || t1.y==t0.y;
		int segment_height = second_half ? t2.y-t1.y : t1.y-t0.y;
		float alpha = (float)i/total_height;
		float beta  = (float)(i-(second_half ? t1.y-t0.y : 0))/segment_height; // be careful: with above conditions no division by zero here
		Vec2i A =               t0 + (t2-t0)*alpha;
		Vec2i B = second_half ? t1 + (t2-t1)*beta : t0 + (t1-t0)*beta;
		if (A.x>B.x) std::swap(A, B);
		for (int j=A.x; j<=B.x; j++) {
			image.set(j, t0.y+i, color); // attention, due to int casts t0.y+i != A.y
		}
	}
}

/*三角形重心 判断点是否在三角形内*/
Vec3f barycentric(Vec2i* pts, Vec2i P)
{
	// Vec3f xVector = Vec3f(pts[2].x - pts[0].x, pts[1].x - pts[0].x, pts[0].x - P.x);
	// Vec3f yVector = Vec3f(pts[2].y - pts[0].y, pts[1].y - pts[0].y, pts[0].y - P.y);
	Vec3f xVector = Vec3f(pts[1].x - pts[0].x, pts[2].x - pts[0].x, pts[0].x - P.x);
	Vec3f yVector = Vec3f(pts[1].y - pts[0].y, pts[2].y - pts[0].y, pts[0].y - P.y);

	Vec3f u = xVector^yVector;

	//if(std::abs(u.z) < 1)
		//return Vec3f(-1,1,1);
	//P点在三角形重心坐标系下的 权重向量
	return Vec3f(1.f - (u.x + u.y)/u.z, u.y/u.z, u.x/u.z);
}

Vec3f barycentric(Vec3f* pts, Vec3f P)
{
	// Vec3f xVector = Vec3f(pts[2].x - pts[0].x, pts[1].x - pts[0].x, pts[0].x - P.x);
	// Vec3f yVector = Vec3f(pts[2].y - pts[0].y, pts[1].y - pts[0].y, pts[0].y - P.y);
	Vec3f xVector = Vec3f(pts[1].x - pts[0].x, pts[2].x - pts[0].x, pts[0].x - P.x);
	Vec3f yVector = Vec3f(pts[1].y - pts[0].y, pts[2].y - pts[0].y, pts[0].y - P.y);

	Vec3f u = xVector^yVector;

	//if(std::abs(u.z) < 1)
	//return Vec3f(-1,1,1);
	//P点在三角形重心坐标系下的 权重向量
	return Vec3f(1.f - (u.x + u.y)/u.z, u.y/u.z, u.x/u.z);
}

//边界框裁剪法
void triangle(Vec2i* pts, TGAImage& image, TGAColor color)
{
	Vec2i bboxmin(image.get_width() - 1, image.get_height() - 1);
	Vec2i bboxmax(0,0);
	//超出image的部分进行裁剪
	Vec2i clamp(image.get_width() - 1, image.get_height() - 1);
	for (int i = 0; i < 3; i++)
	{
		bboxmin.x = std::max(0, std::min(bboxmin.x, pts[i].x));
		bboxmin.y = std::max(0, std::min(bboxmin.y, pts[i].y));

		bboxmax.x = std::min(clamp.x, std::max(bboxmax.x, pts[i].x));
		bboxmax.y = std::min(clamp.y, std::max(bboxmax.y, pts[i].y));		
	}
	Vec2i P;
	for(P.x = bboxmin.x; P.x <= bboxmax.x; P.x++)
	{
		for(P.y = bboxmin.y; P.y <= bboxmax.y; P.y++)
		{
			Vec3f bc_screen = barycentric(pts, P);
			if(bc_screen.x < 0 || bc_screen.y < 0 || bc_screen.z < 0)
				continue;
			image.set(P.x, P.y, color);
		}
	}
}

// zBuffer消隐绘制三角形
void triangle(Vec3f* pts, float* zbuffer, TGAImage& image, TGAColor color)
{
	Vec2f bboxmin(image.get_width() - 1, image.get_height() - 1);
	Vec2f bboxmax(0,0);
	//超出image的部分进行裁剪
	Vec2f clamp(image.get_width() - 1, image.get_height() - 1);
	for (int i = 0; i < 3; i++)
	{
		bboxmin.x = std::max(0.f, std::min(bboxmin.x, pts[i].x));
		bboxmin.y = std::max(0.f, std::min(bboxmin.y, pts[i].y));

		bboxmax.x = std::min(clamp.x, std::max(bboxmax.x, pts[i].x));
		bboxmax.y = std::min(clamp.y, std::max(bboxmax.y, pts[i].y));			
	}
	Vec3f P;
	for(P.x = bboxmin.x; P.x <= bboxmax.x; P.x++)
	{
		for(P.y = bboxmin.y; P.y <= bboxmax.y; P.y++)
		{
			Vec3f bc_screen = barycentric(pts, P);
			if(bc_screen.x < 0 || bc_screen.y < 0 || bc_screen.z < 0)
				continue;
			P.z = 0;
			//根据重心坐标，计算P点的z值
			for (int i = 0; i < 3; i++)
				bc_screen.z += pts[i].z * bc_screen[i];
			if(zbuffer[int(P.x + P.y*width)] < P.z)
			{
				zbuffer[int(P.x + P.y*width)] = P.z;
                image.set(P.x, P.y, color);
			}
		}
	}
}

/* 输出单个像素*/
namespace Section0
{
	void Draw()
	{
		TGAImage image(100, 100, TGAImage::RGB);
		image.set(52, 30, red);
		image.flip_vertically(); // 图片的左下角作为原点
		image.write_tga_file("output.tga");
	}
}

/* 线框模型*/
namespace Section1
{
	void Draw()
	{
		Model *model = NULL;
		model = new Model("../Obj/african_head/african_head.obj");

		TGAImage image(width, height, TGAImage::RGB);
		for (int i = 0; i < model->nfaces(); i++)
		{
			std::vector<int> face = model->face(i);
			for (int j = 0; j < 3; j++)
			{	
				Vec3f v0 = model->vert(face[j]);
				Vec3f v1 = model->vert(face[(j + 1) % 3]);
				int x0 = (v0.x + 1.) * width / 2.;
				int y0 = (v0.y + 1.) * height / 2.;
				int x1 = (v1.x + 1.) * width / 2.;
				int y1 = (v1.y + 1.) * height / 2.;
				line(x0, y0, x1, y1, image, white);
			}
		}

		image.flip_vertically();  // 图片的左下角作为原点
		image.write_tga_file("output.tga");
		delete model;
	}
}

//* 填充三角形*/
namespace Section2
{
	// 平面着色填充
	void Draw1()
	{
		Model *model = NULL;
		model = new Model("../Obj/african_head/african_head.obj");

		TGAImage image(width, height, TGAImage::RGB);
		Vec3f light_dir(0,0,-1);
		for (int i=0; i<model->nfaces(); i++) {
			std::vector<int> face = model->face(i);
			Vec2i screen_coords[3];
			Vec3f world_coords[3];
			for (int j=0; j<3; j++) {
				Vec3f v = model->vert(face[j]);
				screen_coords[j] = Vec2i((v.x+1.)*width/2., (v.y+1.)*height/2.);
				world_coords[j]  = v;
			}
			Vec3f n = (world_coords[2]-world_coords[0])^(world_coords[1]-world_coords[0]);
			n.normalize();
			float intensity = n*light_dir;
			if (intensity>0) {
				triangle(screen_coords[0], screen_coords[1], screen_coords[2], image, TGAColor(intensity*255, intensity*255, intensity*255, 255));
			}
		}

		image.flip_vertically(); // i want to have the origin at the left bottom corner of the image
		image.write_tga_file("output.tga");
		delete model;
	}

	//随机纯色填充三角形
	void Draw2()
	{
		Model *model = NULL;
		model = new Model("../Obj/african_head/african_head.obj");

		TGAImage image(width, height, TGAImage::RGB);
		Vec3f light_dir(0,0,-1);
		for (int i=0; i<model->nfaces(); i++)
		{
			std::vector<int> face = model->face(i);
			Vec2i screen_coords[3];
			for (int j=0; j<3; j++) {
				Vec3f v = model->vert(face[j]);
				screen_coords[j] = Vec2i((v.x+1.)*width/2., (v.y+1.)*height/2.);
			}
			
			//triangle(screen_coords[0], screen_coords[1], screen_coords[2], image, TGAColor(rand()%255, rand()%255, rand()%255, 255));
			triangle(screen_coords, image, TGAColor(rand()%255, rand()%255, rand()%255, 255));
		}

		image.flip_vertically(); // i want to have the origin at the left bottom corner of the image
		image.write_tga_file("output.tga");
		delete model;
	}
}

// zBuffer消隐
namespace Section3
{
	void Draw()
	{
		Model *model = NULL;
		model = new Model("../Obj/african_head/african_head.obj");

		float *zbuffer = new float[width*height];
		for (int i=width*height; i--; zbuffer[i] = -std::numeric_limits<float>::max());

		TGAImage image(width, height, TGAImage::RGB);
		Vec3f light_dir(0,0,-1);
		for (int i=0; i<model->nfaces(); i++) {
			std::vector<int> face = model->face(i);
			Vec3f screen_coords[3];
			Vec3f world_coords[3];
			for (int i=0; i<3; i++)
			{
				screen_coords[i] = world2screen(model->vert(face[i]));
				world_coords[i] = model->vert(face[i]);
			}
			Vec3f n = (world_coords[2]-world_coords[0])^(world_coords[1]-world_coords[0]);
			n.normalize();
			float intensity = n*light_dir;
			if(intensity > 0)
				triangle(screen_coords, zbuffer, image, TGAColor(intensity*255, intensity*255, intensity*255, 255));
		}

		image.flip_vertically(); // i want to have the origin at the left bottom corner of the image
		image.write_tga_file("output.tga");
		delete model;
	}
}

int main(int argc, char** argv)
{
	/*-> TODO float赋值给int */
	int x = 48.3;
	std::cout<<x<<std::endl; // 48

	//Section0::Draw();

	//Section1::Draw();

	//Section2::Draw1();
	//Section2::Draw2();

	Section3::Draw();
	
	return 0;
}



